/**
 *
 */
package com.p2p.common.utils;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.sql.Timestamp;
import java.util.Random;

import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.math.NumberUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.druid.util.StringUtils;

/**
 * head(8)|uniqueKey(128)[key(32)appId(32)]|jam(1)
 * TOKEN生成算法，key 倒推算法
 *
 * @author lizhuolun
 */
public final class TokenUtils {

    private TokenUtils() {
    }

    private static final Logger logger = LoggerFactory.getLogger(TokenUtils.class);

    private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5',
            '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
    private static final String T_VERSION = "A";

    public static String generateToken(Long key) {
        if (key <= 0) {
            return null;
        }
        Random rd = new Random();
        Long rdLong = rd.nextLong();
        int safe = 0;
        while (rdLong < 1000 && safe < 100) {
            rdLong = rd.nextLong();
            safe++;
        }
        //获取当前时间戳转字符串 截取掉前4位
        String nowStr = Long.toString(System.currentTimeMillis()).substring(4);
        //随机Long类型转字符串保留后四位
        String rdLongStr = Long.toString(rdLong);
        rdLongStr = rdLongStr.substring(rdLongStr.length() - 4);

        String mix = mixContent(T_VERSION + rdLongStr + nowStr, Long.toString(key));
        String token = null;
        try {
            token = T_VERSION + Base64.encodeBase64String(mix.getBytes("UTF-8"));
            token = token.replace("\n", "").replace("\r", "").replace("/", "-").replace("+", "_").replace("=", ".");
        } catch (Exception e) {
            logger.error("generateToken", e);
        }

        return token;
    }

    public static long decryptToken(String token) {
        long key = 0;
        if (StringUtils.isEmpty(token)) {
            return key;
        }
        try {
            token = token.substring(1);
            token = token.replace("-", "/").replace("_", "+").replace(".", "=");
            byte[] eb = Base64.decodeBase64(token);
            String dStr = new String(eb, "UTF-8");
            key = getKeyFromMix(dStr);
        } catch (Exception e) {
            logger.error("decryptToken", e);
        }
        return key;
    }

    public static byte[] serialize(Object object) {
        ObjectOutputStream oos = null;
        ByteArrayOutputStream baos = null;
        try {
            //序列化
            baos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(baos);
            oos.writeObject(object);
            byte[] bytes = baos.toByteArray();
            return bytes;
        } catch (Exception e) {
            logger.error("serialize", e);
        }
        return null;
    }

    public static Object unserialize(byte[] bytes) {
        ByteArrayInputStream bais = null;
        try {
            //反序列化
            bais = new ByteArrayInputStream(bytes);
            ObjectInputStream ois = new ObjectInputStream(bais);
            return ois.readObject();
        } catch (Exception e) {

        }
        return null;
    }

    private static String mixContent(String maskStr, String keyStr) {
        if (maskStr.length() <= keyStr.length()) {
            return null;
        }
        char[] mix = new char[maskStr.length() + keyStr.length() + 1];
        int keyStrIdx = 0;
        int maskStrIdx = 0;
        boolean isKeyFull = false;
        for (int i = 0; i < mix.length; i++) {
            if (i % 2 != 0 && keyStrIdx < keyStr.length()) {
                // 奇数位放key
                mix[i] = keyStr.charAt(keyStrIdx);
                keyStrIdx++;
            } else {
                if (keyStrIdx == keyStr.length() && !isKeyFull) {
                    mix[i] = '_';
                    isKeyFull = true;
                    continue;
                }
                mix[i] = maskStr.charAt(maskStrIdx);
                maskStrIdx++;
            }
        }

        return new String(mix);
    }

    private static long getKeyFromMix(String mix) {
        long key = 0;
        if (StringUtils.isEmpty(mix)) {
            return key;
        }
        String[] uMixArr = mix.split("_");
        if (uMixArr == null || uMixArr.length != 2) {
            return key;
        }
        String uStr = "";
        for (int i = 0; i < uMixArr[0].length(); i++) {
            if (i % 2 != 0) {
                uStr += uMixArr[0].charAt(i);
            }
        }
        key = NumberUtils.toLong(uStr);
        return key;
    }

    public static String encodeBySHA1(String str) {
        if (str == null) {
            return null;
        }
        try {
            MessageDigest messageDigest = MessageDigest.getInstance("SHA1");
            messageDigest.update(str.getBytes());
            return getFormattedText(messageDigest.digest());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * encode string
     *
     * @param algorithm
     * @param str
     * @return String
     */
    public static String encode(String algorithm, String str) {
        if (str == null) {
            return null;
        }
        try {
            MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
            messageDigest.update(str.getBytes());
            return getFormattedText(messageDigest.digest());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }

    /**
     * Takes the raw bytes from the digest and formats them correct.
     *
     * @param bytes the raw bytes from the digest.
     * @return the formatted bytes.
     */
    private static String getFormattedText(byte[] bytes) {
        int len = bytes.length;
        StringBuilder buf = new StringBuilder(len * 2);
        // 把密文转换成十六进制的字符串形式
        for (int j = 0; j < len; j++) {
            buf.append(HEX_DIGITS[(bytes[j] >> 4) & 0x0f]);
            buf.append(HEX_DIGITS[bytes[j] & 0x0f]);
        }
        return buf.toString();
    }

    public static void main(String[] s) throws IOException, InvalidKeyException,
            NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException,
            BadPaddingException {

        String t = generateToken(144l);
        System.out.println(t);
        long uid = decryptToken(t);
        System.out.println(uid);

         Timestamp timestamp = new Timestamp(System.currentTimeMillis());

        String dateStr = DateUtil.long2DateStr(timestamp.getTime(), DateUtil.FORMAT_DATE);
        String table1 = "p2p_mac_" + dateStr.replaceAll("(\\d{4})-(\\d{2}).+", "$1$2");
        System.out.println(table1);

    }

}
